namespace MicroFrameworkDriverLibrary.Devices
{
    using System;
    using Microsoft.SPOT.Hardware;

    public abstract class I2CDeviceHelper : IDisposable
    {
        protected bool _disposed = false;
        protected readonly I2CDevice _sharedI2C;
        protected readonly I2CDevice.Configuration _configuration;
        protected readonly int _transactionTimeoutMs;

        public I2CDeviceHelper(I2CDevice sharedI2C, ushort hardwareAddress, int clockRateKHz, int transacitonTimeoutMs)
        {
            this._sharedI2C = sharedI2C;
            this._transactionTimeoutMs = transacitonTimeoutMs;

            // MF requires the address as a 7Bit address .. not 8 ... so make sure that is what we are using.
            this._configuration = new I2CDevice.Configuration((ushort)(hardwareAddress & 0x7F), clockRateKHz);
        }

        protected delegate void Action();
        protected void SwapConfiguration(Action i2cAction)
        {
            // There is no shared lock on the I2C instance, so just use the class instance itself (not recomended, but its all we have)
            lock (this._sharedI2C)
            {
                // Get the current configuration for the I2C and save it locally so we can put it back.
                var originalConfiguration = this._sharedI2C.Config;
                try
                {
                    // Swap in our new configuration
                    this._sharedI2C.Config = this._configuration;

                    // Run the action for this configuration;
                    i2cAction();
                }
                finally
                {
                    // Put the original configurationback in place.
                    this._sharedI2C.Config = originalConfiguration;
                }
            }
        }

        protected static int BcdToDec(int value)
        {
            return ((value / 16 * 10) + (value % 16));
        }

        protected static byte DecToBcd(int value)
        {
            return (byte)((value / 10 * 16) + (value % 10));
        }

        protected static bool GetBit(byte value, byte bitToCheck)
        {
            return (value & (1 << bitToCheck)) != 0;
        }

        protected static byte SetBit(byte value, byte bitToSet, bool state)
        {
            if (state)
            {
                // Set the bit ON
                return (byte)(value | (1 << bitToSet));
            }

            // Set the bit OFF (clear it)
            return (byte)(value & ~(1 << bitToSet));
        }

        protected static byte ToggleBit(byte value, byte bitToToggle)
        {
            return (byte)(value ^ (1 << bitToToggle));
        }

        public void Dispose()
        {
            this.Dispose(true);
        }

        protected abstract void Dispose(bool disposing);
    }
}
